﻿using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System;

    public class GameMapEditorPanel
    {
        static GameMapEditorPanel _inst;
        public static GameMapEditorPanel Inst
        {
            get { if (_inst == null) { _inst = new GameMapEditorPanel(); } return _inst; }
            set { _inst = value; }
        }

        public int SceneMapControlId = "MapEditor".GetHashCode();
        public Plane MapEditorPlane;
        GameObject G;
        GameObject SelectBrush;
        GameObject DrawBrushRoot;
        Dictionary<string, GameObject> DrawBrushGs;

        MapStaticBlock SeletBlockData;
        GameObject[] layerGs;

        bool IsMouseDowning = false;
        Vector3 MouseDownPos;
        int MouseDownPosX;
        int MouseDownPosY;
        int DragingPosX;
        int DragingPosY;

        public void Load()
        {
            if (this.G != null || GameObject.Find("SceneMap") != null)
            {
                GameObject.DestroyImmediate(GameObject.Find("SceneMap"));
            }
            this.IsMouseDowning = false;
            this.G = new GameObject("SceneMap");
            GameObject LightG = new GameObject("Light");
            Light light = LightG.AddComponent<Light>();
            light.type = LightType.Directional;
            light.transform.parent = this.G.transform;
            light.transform.rotation = Quaternion.Euler(50, -50, 0);
            LightG.transform.position = new Vector3(-9999f, -9999f, 0);
            this.SeletBlockData = null;

            this.DrawBrushRoot = new GameObject("DrawBrush");
            this.DrawBrushRoot.transform.parent = this.G.transform;
            this.DrawBrushGs = new Dictionary<string, GameObject>();

            GameMapData mapData = GameMapMakerEditor.MapData;
            MapEditorPlane = new Plane(Vector3.zero, new Vector3(mapData.Header.MapWidth * mapData.Header.TileSizeX, 0, 0), new Vector3(0, mapData.Header.MapHeight * mapData.Header.TilesizeY, 0));
            this.LoadBlocks();
        }

        void LoadBlocks()
        {
            GameMapData mapData = GameMapMakerEditor.MapData;
            layerGs = new GameObject[mapData.LayerDatas.Length];
            for (int i = 0; i < mapData.LayerDatas.Length; i++)
            {
                GameObject LayerG = new GameObject("Layer_" + mapData.LayerDatas[i].Desc);
                layerGs[i] = LayerG;
                LayerG.transform.parent = this.G.transform;
                LoadBlockLayer(LayerG.transform, i);
            }
        }

        void LoadBlockLayer(Transform LayerT, int layerID)
        {
            GameMapData mapData = GameMapMakerEditor.MapData;
            GameMapData.MapLayerData layerData = mapData.LayerDatas[layerID];
            for (int y = 0; y < mapData.Header.MapHeight; y++)
            {
                for (int x = 0; x < mapData.Header.MapWidth; x++)
                {

                    MapBlock block = layerData.MapBlocks[y, x];
                    if (block != null && block.PosY == y && block.PosX == x)
                    {
                        string PrefabPath = block.StaticBlock.PrefabPath;
                        GameObject newBlockG = (GameObject)GameObject.Instantiate(Resources.Load(PrefabPath));
                        newBlockG.transform.parent = LayerT;
                        newBlockG.name = x.ToString() + "_" + y.ToString();
                        newBlockG.transform.localPosition = this.CalculatePosByMapPos(x, y, block.StaticBlock);
                    }
                }
            }
        }

        public void OnSelectBlock(int blockId)
        {

            GameMapData data = GameMapMakerEditor.MapData;
            if (MapBlockData.Inst.Blocks.ContainsKey(blockId))
            {
                this.SeletBlockData = MapBlockData.Inst.Blocks[blockId];
                if (this.SelectBrush == null || this.SelectBrush.name != blockId.ToString())
                {
                    if (this.SelectBrush != null)
                    {
                        GameObject.DestroyImmediate(this.SelectBrush);
                    }
                    this.SelectBrush = (GameObject)GameObject.Instantiate(Resources.Load(MapBlockData.Inst.Blocks[blockId].PrefabPath));
                    this.SelectBrush.transform.parent = this.G.transform;
                    this.SelectBrush.transform.position = new Vector3(-99999f, -99999f, 0);
                    this.SelectBrush.name = blockId.ToString();
                    
                }
            }
            else if (blockId == -1)
            {
                DestoryBrush();
                this.SelectBrush = GameObject.CreatePrimitive(PrimitiveType.Cube);
                this.SelectBrush.transform.parent = this.G.transform;
                this.SelectBrush.transform.localScale = new Vector3(data.Header.TileSizeX, data.Header.TilesizeY, data.Header.TileSizeX);
                this.SelectBrush.transform.position = new Vector3(-99999f, -99999f, 0);
                this.SelectBrush.name = "-1";
            }
        }

        public void Draw()
        {
            this.DrawMapBGLine();
            this.UpdateBrush();
            this.UpdateHitPos();

        }

        void DrawMapBGLine()
        {
            if (GameMapMakerEditor.MapData == null)
            {
                return;
            }
            GameMapData mapData = GameMapMakerEditor.MapData;

            Handles.color = Color.gray;
            for (int x = 0; x < GameMapMakerEditor.MapData.Header.MapWidth + 1; x++)
            {
                Handles.DrawLine(new Vector3(x * mapData.Header.TileSizeX, 0, 0), new Vector3(x * mapData.Header.TileSizeX, mapData.Header.MapHeight * mapData.Header.TilesizeY, 0));
            }
            for (int y = 0; y < GameMapMakerEditor.MapData.Header.MapHeight + 1; y++)
            {
                Handles.DrawLine(new Vector3(0, y * mapData.Header.TilesizeY, 0), new Vector3(mapData.Header.MapWidth * mapData.Header.TileSizeX, y * mapData.Header.TilesizeY, 0));
            }
        }

        bool IsInPanel(Vector3 v3)
        {
            GameMapData mapData = GameMapMakerEditor.MapData;
            float MaxX = mapData.Header.TileSizeX * mapData.Header.MapWidth;
            float MaxY = mapData.Header.TilesizeY * mapData.Header.MapHeight;
            if (v3.x > 0 && v3.y > 0 && v3.x < MaxX && v3.y < MaxY)
            {
                return true;
            }
            return false;
        }

        bool OnInSidePos(System.Action<Vector3> fn = null)
        {
            Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
            float hitD = 0.0f;
            if (MapEditorPlane.Raycast(ray, out hitD))
            {
                Vector3 hitPos = ray.GetPoint(hitD);

                if (this.IsInPanel(hitPos))
                {
                    if (fn != null) fn(hitPos);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            return false;
        }


        void UpdateHitPos()
        {
            EventType controlEventType = Event.current.GetTypeForControl(SceneMapControlId);
            if (controlEventType == EventType.MouseDown && Event.current.button == 0)
            {
                this.OnInSidePos((pos) =>
                {
                    if (this.SelectBrush == null)
                    {
                        return;
                    }
                    EditorGUIUtility.hotControl = this.SceneMapControlId;
                    this.IsMouseDowning = true;
                    this.GetMapBlockPosIndex(pos, out this.MouseDownPosX, out this.MouseDownPosY);
                    this.MouseDownPos = this.CalculatePosByMapPos(this.MouseDownPosX, this.MouseDownPosY, this.SeletBlockData);
                    this.DragingPosX = this.MouseDownPosX;
                    this.DragingPosY = this.MouseDownPosY;
                });
            }
            else if (EditorGUIUtility.hotControl == this.SceneMapControlId)
            {
                EditorGUIUtility.hotControl = 0;
            }
            else if (Event.current.button == 1)
            {
                this.DestoryBrush();
            }
        }

        void AddBlockToMap(int layerId)
        {
            //擦除
            if (this.SeletBlockData == null)
            {
                EachDrawBrushBlock(this.MouseDownPosX, this.MouseDownPosY, this.DragingPosX, this.DragingPosY, (curx, cury) =>
                {
                    MapBlock block = GameMapMakerEditor.MapData.LayerDatas[layerId].MapBlocks[cury, curx];
                    if (block!=null&&block.PosX == curx && block.PosY == cury)
                    {
                        Transform BlockT = layerGs[layerId].transform.Find(block.PosX.ToString() + "_" + block.PosY.ToString());
                        GameObject.DestroyImmediate(BlockT.gameObject);
                        GameMapMakerEditor.MapData.LayerDatas[layerId].RemoveBlock(curx, cury);
                    }
                });
            }
            //添加
            else
            {
                EachDrawBrushBlock(this.MouseDownPosX, this.MouseDownPosY, this.DragingPosX, this.DragingPosY, (curx,cury)=> 
                {
                    if (IsCanDrawBrushBlock(curx, cury)&& GameMapMakerEditor.MapData.LayerDatas[layerId].MapBlocks[cury, curx]==null)
                    {
                        GameObject Obj = (GameObject)GameObject.Instantiate(this.SelectBrush);
                        Obj.transform.parent = this.layerGs[layerId].transform;
                        Obj.transform.localScale = this.SelectBrush.transform.localScale;
                        Obj.transform.position = this.CalculatePosByMapPos(curx,cury,this.SeletBlockData);
                        Obj.name = curx.ToString() + "_" + cury.ToString();
                        GameMapMakerEditor.MapData.LayerDatas[layerId].AddBlock(this.SeletBlockData.Id, curx, cury);
                    }
                });
            }
        }

        bool CanPlaceBlock(GameMapData.MapLayerData layerData, MapStaticBlock blockData, int x, int y)
        {
            for (int yi = 0; yi < blockData.Heigth; yi++)
            {
                for (int xi = 0; xi < blockData.Width; xi++)
                {
                    int newX = x + xi;
                    int newY = y + yi;
                    if (newX < layerData.mapData.Header.MapWidth && newY < layerData.mapData.Header.MapHeight && layerData.GetBlockIndex(newY, newX) != -1)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        void UpdateBrush()
        {
            if (this.SelectBrush == null)
            {
                return;
            }

            this.OnInSidePos((pos) =>
            {
                this.SelectBrush.transform.position = this.CalculatePosToBlockPos(pos);

                EventType controlEventType = Event.current.GetTypeForControl(SceneMapControlId);
                if (this.IsMouseDowning)
                {
                    if (controlEventType == EventType.MouseDrag)
                    {
;                       if (this.SelectBrush.activeSelf)
                        {
                            this.SelectBrush.SetActive(false);
                        }

                        this.GetMapBlockPosIndex(pos, out this.DragingPosX, out this.DragingPosY);
                        Vector3 CurPos = this.CalculatePosByMapPos(DragingPosX, DragingPosY, this.SeletBlockData);
                        fillDrawBrush(this.MouseDownPosX, this.MouseDownPosY, DragingPosX, DragingPosY);
                    }
                    else if (controlEventType == EventType.MouseUp)
                    {
                        while (DrawBrushRoot.transform.childCount > 0)
                        {
                            GameObject.DestroyImmediate(this.DrawBrushRoot.transform.GetChild(0).gameObject);
                        }
                        this.DrawBrushGs.Clear();
                        this.IsMouseDowning = false;
                        if (!this.SelectBrush.activeSelf)
                        {
                            this.SelectBrush.SetActive(true);
                        }
                        AddBlockToMap(GameMapMakerEditor.MapData.CurEditLayout);
                    }
                }
            });
        }

        void fillDrawBrush(int oldX, int oldY, int newX, int newY)
        {   
            foreach (KeyValuePair<string, GameObject> Item in this.DrawBrushGs)
            {
                Item.Value.SetActive(false);
            }
            EachDrawBrushBlock(oldX, oldY, newX, newY,OnEachDrawBrushBlock);  
        }

        bool IsCanDrawBrushBlock(int curx, int cury)
        {
            int BlockW = SeletBlockData == null ? 1 : SeletBlockData.Width;
            int BlockH = SeletBlockData == null ? 1 : SeletBlockData.Heigth;
            int Xsub = (curx - MouseDownPosX) % BlockW;
            int Ysub = (cury - MouseDownPosY) % BlockH;
            if (Xsub == 0 && Ysub == 0)
            {
                return true;
            }
            return false;
        }

        void OnEachDrawBrushBlock(int curx,int cury)
        { 
            if (IsCanDrawBrushBlock(curx,cury))
            {
                string mapKey = curx.ToString() + "_" + cury.ToString();
                if (this.DrawBrushGs.ContainsKey(mapKey))
                {
                    this.DrawBrushGs[mapKey].SetActive(true);
                }
                else
                {
                    GameObject NewObj = (GameObject)GameObject.Instantiate(this.SelectBrush);
                    NewObj.transform.parent = this.DrawBrushRoot.transform;
                    NewObj.transform.position = this.CalculatePosByMapPos(curx, cury, this.SeletBlockData);
                    this.DrawBrushGs[mapKey] = NewObj;
                }
            }
        }

        

        void EachDrawBrushBlock(int oldX, int oldY, int newX, int newY, Action<int, int> callfn)
        {
            int subX = newX - oldX;
            int subY = newY - oldY;
            Action<Action<int, Action<int, int>>, Action<int, int>> EachX = null;
            Action<int, Action<int, int>> EachY = null;
            if (subX <= 0)
            {
                EachX = (eachY, fn) => { for (int x = oldX; x >= newX; x--) { EachY(x, fn); } };
            }
            else
            {
                EachX = (eachY, fn) => { for (int x = oldX; x <= newX; x++) { EachY(x, fn); } };
            }

            if (subY <= 0)
            {
                EachY = (nowX, fn) => { for (int y = oldY; y >= newY; y--) { fn(nowX, y); } };
            }
            else
            {
                EachY = (nowX, fn) => { for (int y = oldY; y <= newY; y++) { fn(nowX, y); } };
            }
            EachX(EachY, (x, y) =>{ callfn(x,y);});
        }

        Vector3 CalculatePosToBlockPos(Vector3 pos)
        {
            Vector3 retPos = pos;
            GameMapData data = GameMapMakerEditor.MapData;
            int x, y;
            this.GetMapBlockPosIndex(pos, out x, out y);
            retPos = CalculatePosByMapPos(x,y,this.SeletBlockData);
            return retPos;
        }

        Vector3 CalculatePosByMapPos(int x,int y,MapStaticBlock blockData)
        {
            GameMapData data = GameMapMakerEditor.MapData;
            int WSize = 1;
            int HSize = 1;
            if (blockData != null)
            {
                WSize = blockData.Width;
                HSize = blockData.Heigth;
            }

            Vector3  retPos = new Vector3(
                x * data.Header.TileSizeX + (data.Header.TileSizeX * WSize) * 0.5f,
                y * data.Header.TilesizeY + (data.Header.TilesizeY * HSize) * 0.5f,
                data.Header.TilesizeY * 0.5f
             );
            return retPos;
        }
    
        void DestoryBrush()
        {
            if (this.SelectBrush)
            {
                GameObject.DestroyImmediate(this.SelectBrush);
            }
            this.SelectBrush = null;
            this.SeletBlockData = null;
        }

        void GetMapBlockPosIndex(Vector3 pos, out int X, out int Y)
        {
            GameMapData data = GameMapMakerEditor.MapData;
            X = (int)(pos.x / (float)data.Header.TileSizeX);
            Y = (int)(pos.y / (float)data.Header.TilesizeY);
        }


    }

